#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>

#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
#define NO_REDIR 0

int g_redir = NO_REDIR;

char* checkdir(char* cmd)
{
    assert(cmd);

    char* end = cmd + strlen(cmd) - 1;
    while (end >= cmd)
    {
        //检测重定向符号
        if (*end == '<')
        {
            g_redir = INPUT_REDIR;
            *end = '\0';
            end++;
            break;
        }
        else if (*end == '>')
        {
            //追加重定向
            if (*(end - 1) == '>')
            {
                g_redir = APPEND_REDIR;
                *(end - 1) = '\0';
                end++;
                break;
            }
            g_redir = OUTPUT_REDIR;
            *end = '\0';
            end++;
            break;
        }
        else{
            end--;
        }
    }

    if (end >= cmd)
    {
        return end;
    }
    else 
    {
        return NULL;
    } 
}

//shell运行原理，通过让子进程执行命令,父进程等待 解析命令
int main()
{
    char* ary[64];
    char _env[64]; //记录环境变量
    //0. 命令解释器,是一个常驻进程,不退出
    while (1)
    {
        //1. 打印提示信息 [tmp@VM-16-7-cento myshell]#
        printf("[tmp@VM-16-7-centos myshell]# ");
        fflush(stdout);
        //2. 获取用户输入的指令和选项
        char buff[1024];
        memset(buff, '\0', sizeof buff);
        if (fgets(buff, sizeof buff, stdin) == NULL)
        {
            continue;
        }

        //printf("%s\n", buff);
        //缓冲区的回车去掉
        buff[strlen(buff) - 1] = '\0';
        //分析是否有重定向, "ls -a -l>log.txt"
        char* sep = checkdir(buff);
        //3. 分割命令行,解析
        ary[0] = strtok(buff, " ");   //第一次调用,传入原始字符串
        int index = 1;
        //ls增加颜色
        if (strcmp(ary[0], "ls") == 0)
        {
            ary[index++] = "--color=auto";
        }
        while(ary[index] = strtok(NULL, " "))
        {
            index++;
        }
        //3.5 内置命令,需要父进程执行的,如cd
        if (strcmp(ary[0], "cd")==0)
        {
            if (ary[1] != NULL)
            {
                chdir(ary[1]);
                continue;
            }
        }

        if (strcmp(ary[0], "export") == 0 && ary[1] != NULL)
        {
            strcpy(_env, ary[1]);
            putenv(_env);
            continue;
        }
        pid_t id = fork();
        if (id == 0)
        {
        if (sep != NULL)
        {
            //重定向
            //switch里不能定义变量
            int fd = 0;
            switch(g_redir)
            {
               case INPUT_REDIR:
                    fd = open(sep, O_RDONLY);
                    dup2(fd, 0);
                   break;
               case OUTPUT_REDIR:
                   fd = open(sep, O_WRONLY | O_CREAT | O_TRUNC, 0666);
                   dup2(fd, 1);
                   break;
               case APPEND_REDIR:
                   fd = open(sep, O_WRONLY | O_CREAT | O_APPEND);
                   dup2(fd, 1);
                   break;
                default:
                   printf("error\n");
                   break;
            }
        }
            //4. 子进程执行任务
            execvp(ary[0], ary);
            exit(1);
        }
        //父进程等待
        int status = 0;
       pid_t ret =  waitpid(id,&status,0 );
       if (ret > 0)
       {
           printf("等待成功,退出码: %d\n", WEXITSTATUS(status));
       }
       else
       {
           printf("等到失败\n");
       }
    }
    return 0;
}

